package org.glutenberg.dao;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.glutenberg.Handbook;
import org.glutenberg.util.CollectionUtils;
import org.glutenberg.util.LinkedHashtable;
import org.glutenberg.util.Map;
import org.glutenberg.util.RandomAccessStreamReader;
import org.glutenberg.util.StringComparator;
import org.glutenberg.util.StringUtils;
import org.glutenberg.util.TreeMap;

public class AlphabeticIndex {

	private RandomAccessStreamReader reader;
	private long indexSize = 0;
	private long end;
	private long end2;
	private boolean hasGroupIndex = false;

	private Hashtable index;
	private LinkedHashtable groupIndex;
	
	public AlphabeticIndex(RandomAccessStreamReader reader) throws IOException{
		this.index = new Hashtable();
		this.groupIndex = new LinkedHashtable();
		this.reader = reader;
		this.readAndParseAlphabeticalIndex();
	}
	
	private void readAndParseAlphabeticalIndex() throws IOException{
		char c  = (char)reader.read();
		this.hasGroupIndex = '1' == c;
		c  = (char)reader.read();
		this.readIndex();
	}
	
	private void putInIndex(Object key, Object value){
		if(!this.hasGroupIndex){
			this.index.put(key, value);
		} else {
			this.groupIndex.put(key, value);
		}
	}
	
	private void readIndex() throws IOException{
		long indexSize = 0;
		StringBuffer buffer = new StringBuffer();
		char c  = (char)reader.read();
		String key = null;
		String value = null;
		while(c>=0 && c!='\n'){
			if(c=='-'){
				if(key==null) key=buffer.toString();
				else if(value==null){
					value=buffer.toString();
					if(!key.startsWith("end")){
						IndexRecord record = this.createRecord(key, value);
						this.putInIndex(key, record);
					} else if("end".equals(key)){
						this.end = Long.parseLong(value);
					}
					key = null;
					value = null;
				}
				buffer.setLength(0);
			} else {
				buffer.append(c);
			}
			indexSize++;
			c = (char)this.reader.read();
		}
		this.end2 = Long.parseLong(buffer.toString());
		this.indexSize = indexSize + 3;		
	}
	
	private IndexRecord createRecord(String key, String value){
		boolean isListIndex = false;
		if(value.startsWith("i")){
			isListIndex = true;
			value = value.substring(1);
		}
		long index1 = Long.parseLong(value);
		IndexRecord record = new IndexRecord(index1);
		record.setListIndex(isListIndex);
		return record;
	}
	
	public Vector getIndexes(Vector words) throws IOException{
		Vector ret = new Vector();
		if(!this.hasGroupIndex){
			for(int i=0;i<words.size();i++){
				String word = (String)words.elementAt(i);
				IndexRecord record = (IndexRecord)this.index.get(word);
				if(record==null) return ret;
				ret.addElement(record);
			}
		} else {
			TreeMap grouped = new TreeMap();
			Vector groupStarts = this.groupIndex.keysVector();
			int i=0;
			int j=0;
			while(i<words.size() && j<groupStarts.size()-1){
				String word = (String) words.elementAt(i);
				String nextStart = (String) groupStarts.elementAt(j+1);
				if(word.compareTo(nextStart)<0){
					String start = (String) groupStarts.elementAt(j);
					Vector v = new Vector();
					v.addElement(word);
					i++;
					while(i<words.size() && ((String)words.elementAt(i)).compareTo(nextStart)<0){
						v.addElement(words.elementAt(i));
						i++;
					}
					IndexRecord startRecord = (IndexRecord)this.groupIndex.get(start);
					IndexRecord nextStartRecord = (IndexRecord)this.groupIndex.get(nextStart);
					Object[] val = new Object[2];
					val[0] = nextStartRecord;
					val[1] = v;
					grouped.put(startRecord, val);
				}
				j++;
			}
			if(i<words.size() && j==groupStarts.size()-1){
				String start = (String) groupStarts.elementAt(j);
				Vector v = new Vector();
				while(i<words.size()){
					String word = (String)words.elementAt(i);
					v.addElement(word);
					i++;
				}
				Object[] val = new Object[2];
				val[0] = new IndexRecord(this.end2);
				val[1] = v;
				grouped.put(((IndexRecord)this.groupIndex.get(start)), val);
			}
			ret = this.getIndexesFromGroups(grouped);
		}
		return ret;
	}
	
	public Vector getIndexesFromGroups(TreeMap grouped) throws IOException{
		Enumeration groups = grouped.elements();
		Map.Entry entry = null;
		Vector ret = new Vector();
		Vector groupsV =CollectionUtils.fromEnumerationToVector(groups);
		for(int i=0;i<groupsV.size();i++){
			entry = (Map.Entry) groupsV.elementAt(i);
			IndexRecord record = (IndexRecord)entry.getKey();
			
			Object[] val = (Object[])entry.getValue();
			IndexRecord nextRecord = (IndexRecord)val[0];
			Vector words = (Vector)val[1];
			
			this.reader.seek(this.indexSize + record.getIndex());
			while(words.size()>0){
				StringBuffer buf = new StringBuffer();
				char c  = (char)reader.read();
				while(c>=0 && c!='-'){
					buf.append(c);
					c  = (char)reader.read();
				}
				if(c<0) return ret;
				String key = buf.toString();
				buf.setLength(0);
				c  = (char)reader.read();
				while(c>=0 && c!='-' && c!='\n'){
					buf.append(c);
					c  = (char)reader.read();
				}
				if(c<0) return ret;
				String value = buf.toString();
				buf.setLength(0);
				String firstWord = (String) words.elementAt(0);
				if(firstWord.compareTo(key)<0) break;
				while(firstWord.compareTo(key)<0){
					words.removeElementAt(0);
					if(words.size()==0) break;
					firstWord = (String) words.elementAt(0);
				}
				if(firstWord.equals(key)){
					ret.addElement(this.createRecord(key, value));
					words.removeElementAt(0);
				}
				if(c=='\n' || this.reader.getPosition()>=this.indexSize + nextRecord.getIndex()) break;
			}
			if(words.size()>0) break;
		}
		return ret;
	}
	
	public long getReverseIndexEnd(){
		return indexSize + this.end2;
	}
	
	public long getEndIndex() {
		return indexSize + this.end;
	}
}
